package com.gitee.feizns.quickstart.mybatis.flex.query;

import com.gitee.feizns.dynamic.bean.Getter;
import com.gitee.feizns.dynamic.bean.Props;
import com.gitee.feizns.dynamic.reflect.Constructors;
import com.gitee.feizns.dynamic.reflect.Lambdas;
import com.gitee.feizns.quickstart.domain.query.Relationship;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import com.mybatisflex.core.update.UpdateChain;
import com.mybatisflex.core.util.FieldWrapper;
import lombok.SneakyThrows;
import org.springframework.util.CollectionUtils;

import java.lang.invoke.SerializedLambda;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 关系
 * @author feizns
 * @since 2025/8/8
 */
public class MybatisFlexRelationship<T, M, S> implements Relationship<T, M, S> {

    /**
     * 实体类
     */
    @lombok.Getter
    private Class<T> entityClass;

    /**
     * 主字段
     */
    private Getter<T, M> mainField;

    /**
     * 子z字段
     */
    private Getter<T, S> subField;

    /**
     * 主字段名称
     */
    private String mainFieldName;

    /**
     * 子编号田名称
     */
    private String subFieldName;

    /**
     * 主字段包装器
     */
    private FieldWrapper mainFieldWrapper;

    /**
     * 子字段包装器
     */
    private FieldWrapper subFieldWrapper;

    /**
     * 主要编号
     */
    private M mainId;

    /**
     * 过滤器（使用字段:值的方式参与查询与操作）
     */
    private final Map<LambdaColumnParser<T>, Object> filters = new LinkedHashMap<>();

    @Override
    public M getMainId() {
        return mainId != null ? mainId : Relationship.super.getMainId();
    }

    @Override
    public void setMainId(M mainId) {
        this.mainId = mainId;
    }

    /**
     * 关系
     * @param mainField 主字段
     * @param subField  子字段
     */
    @SneakyThrows
    @SuppressWarnings("unchecked")
    public MybatisFlexRelationship(Getter<T, M> mainField, Getter<T, S> subField) {
        this.mainField = mainField;
        this.subField = subField;
        SerializedLambda mainFiledLambda = Lambdas.getSerializedLambda(mainField);
        SerializedLambda subFieldLambda = Lambdas.getSerializedLambda(subField);
        this.entityClass = (Class<T>) Class.forName(mainFiledLambda.getImplClass().replace("/", "."));
        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass);
        String mainProperty = Props.name(mainFiledLambda.getImplMethodName());
        String subProperty = Props.name(subFieldLambda.getImplMethodName());
        this.mainFieldName = tableInfo.getColumnByProperty(mainProperty);
        this.subFieldName = tableInfo.getColumnByProperty(subProperty);
        this.mainFieldWrapper = FieldWrapper.of(entityClass, mainProperty);
        this.subFieldWrapper = FieldWrapper.of(entityClass, subProperty);
    }

    public MybatisFlexRelationship(Getter<T, M> mainField, M mainId, Getter<T, S> subField) {
        this(mainField, subField);
        this.mainId = mainId;
    }

    /**
     * 关联表操作
     * @param mainField 主字段
     * @param subField  子字段
     * @return {@link MybatisFlexRelationship }<{@link T }, {@link M }, {@link S }>
     */
    public static <T, M, S> MybatisFlexRelationship<T, M, S> of(Getter<T, M> mainField, Getter<T, S> subField) {
        return new MybatisFlexRelationship<>(mainField, subField);
    }

    /**
     * 关联表操作
     * @param mainField 主字段
     * @param subField  子字段
     * @return {@link MybatisFlexRelationship }<{@link T }, {@link M }, {@link S }>
     */
    public static <T, M, S> MybatisFlexRelationship<T, M, S> of(Getter<T, M> mainField, M mainId, Getter<T, S> subField) {
        return new MybatisFlexRelationship<>(mainField, mainId, subField);
    }

    /**
     * 过滤器（添加该字段作为条件，新增时也会添加上改字段值）
     * @param field 字段名称
     * @param value 值
     * @return {@link MybatisFlexRelationship }<{@link T }, {@link M }, {@link S }>
     */
    public MybatisFlexRelationship<T, M, S> filter(Getter<T, ?> field, Object value) {
        this.filters.put(LambdaColumnParser.of(field), value);
        return this;
    }

    /**
     * 查询包装器
     * @return {@link QueryWrapper }
     */
    private <U extends QueryWrapper> U buildFilterConditional(U queryWrapper) {
        for (Map.Entry<LambdaColumnParser<T>, Object> entry : filters.entrySet())
            queryWrapper.eq(entry.getKey().getColumnName(), entry.getValue());
        return queryWrapper;
    }

    /**
     * 更新链
     * @return {@link UpdateChain }<{@link T }>
     */
    private UpdateChain<T> updateChain() {
        return buildFilterConditional(UpdateChain.of(entityClass));
    }

    /**
     * 查询链
     * @return {@link QueryChain }<{@link T }>
     */
    private QueryChain<T> queryChain() {
        return buildFilterConditional(QueryChain.of(entityClass));
    }

    /**
     * 重置关系
     * @param id        编号
     * @param newSubIds 子表ID
     * @return boolean
     */
    public boolean reset(M id, Collection<? extends S> newSubIds) {
        //尽可能保留原有关联（避免数据丢失）
        return Db.txWithResult(() -> {
            if ( CollectionUtils.isEmpty(newSubIds) ) {
                return clear(id);
            } else {
                //数据库中现在保留的关联
                List<S> subIds = subIds(id);
                //本次期望重置的关联
                List<S> newSubIdsList = new ArrayList<>(newSubIds);
                Set<S> newSubIdsSet = new HashSet<>(newSubIdsList);
                //筛选需删除的关系
                List<S> deleteSubIds = subIds.stream().filter(subId -> !newSubIdsSet.contains(subId)).collect(Collectors.toList());
                if ( !CollectionUtils.isEmpty(deleteSubIds) )
                    delete(id, deleteSubIds);
                //筛选需添加的关系
                return add(id, newSubIdsList) > 0;
            }
        });
    }

    /**
     * 添加关系
     * @param id        主表编号
     * @param newSubIds 子表ID
     * @return int
     */
    @SafeVarargs
    public final int add(M id, S... newSubIds) {
        return add(id, Arrays.asList(newSubIds));
    }

    /**
     * 添加关系
     * @param id        主表编号
     * @param newSubIds 子表ID
     * @return int
     */
    public int add(M id, Collection<? extends S> newSubIds) {
        List<T> ts = create(id, newSubIds);
        List<S> subIds = subIds(id);
        Set<Object> subIdsSet = new HashSet<>(subIds);
        ts = ts.stream().filter(entity -> !subIdsSet.contains(subField.getter(entity))).collect(Collectors.toList());
        if ( !CollectionUtils.isEmpty(ts) )
            return queryChain().baseMapper().insertBatch(ts);
        return 0;
    }

    /**
     * 删除指定关系
     * @param mainId 主表编号
     * @param subIds 子表ID
     * @return boolean
     */
    @SafeVarargs
    public final boolean delete(M mainId, S... subIds) {
        return delete(mainId, Arrays.asList(subIds));
    }

    /**
     * 删除指定关系
     * @param mainId 主表编号
     * @param subIds 子表ID
     * @return boolean
     */
    public boolean delete(M mainId, Collection<? extends S> subIds) {
        UpdateChain<T> eq = updateChain().eq(mainFieldName, mainId);
        if ( subIds != null ) {
            if ( subIds.size() == 1 )
                eq.eq(subFieldName, subIds.iterator().next());
            else if ( subIds.size() > 1 )
                eq.in(subFieldName, subIds);
            return eq.remove();
        }
        return false;
    }

    /**
     * 清空主表字段为指定值的关系
     * @param mainId 主表编号
     * @return boolean
     */
    public boolean clear(M mainId) {
        return updateChain().eq(mainFieldName, mainId).remove();
    }

    /**
     * 列表（获取所有的指定主表值的所有关联关系）
     * @param mainId 主表编号
     * @return {@link List }<{@link T }>
     */
    public List<T> list(M mainId) {
        return queryChain().eq(mainFieldName, mainId).list();
    }

    /**
     * 获取指定主表值的所有关联关系编号列表
     * @param mainId 主表编号
     * @return {@link List }<{@link S }>
     */
    @SuppressWarnings("unchecked")
    public List<S> subIds(M mainId) {
        return queryChain().eq(mainFieldName, mainId).select(subFieldName).objListAs((Class<S>) subFieldWrapper.getFieldType());
    }

    /**
     * 是否存在指定关系
     * @param mainId    主要编号
     * @param subIds     子编号
     * @return boolean
     */
    @SafeVarargs
    public final boolean exists(M mainId, S... subIds) {
        return exists(mainId, Arrays.asList(subIds));
    }

    /**
     * 是否存在指定关系
     * @param mainId    主要编号
     * @param subIds     子编号
     * @return boolean
     */
    public boolean exists(M mainId, Collection<? extends S> subIds) {
        QueryChain<T> eq = queryChain().eq(mainFieldName, mainId);
        if ( subIds.size() == 1 )
            eq.eq(subFieldName, subIds.iterator().next());
        else if ( subIds.size() > 1 )
            eq.in(subFieldName, subIds);
        return eq.exists();
    }

    /**
     * 计数（获取所有的指定主表值的所有关联关系数量）
     * @param mainId 主表编号
     * @return long
     */
    public long count(M mainId) {
        return queryChain().eq(mainFieldName, mainId).count();
    }

    /**
     * 创造
     * @param mainId main编号
     * @param subId  子编号
     * @return {@link T }
     */
    @SneakyThrows
    public T create(M mainId, S subId) {
        T instance = Constructors.newInstance(entityClass);
        mainFieldWrapper.set(mainId, instance);
        subFieldWrapper.set(subId, instance);
        filters.forEach((field, value) -> field.getFieldWrapper().set(value, instance));
        return instance;
    }

    /**
     * 创造
     * @param mainId    main编号
     * @param subIds    子 ID
     * @return {@link T }
     */
    @SafeVarargs
    public final List<T> create(M mainId, S... subIds) {
        return Arrays.stream(subIds).map(subId -> create(mainId, subId)).collect(Collectors.toList());
    }

    /**
     * 创造
     * @param mainId    main编号
     * @param subIds    子 ID
     * @return {@link T }
     */
    public List<T> create(M mainId, Collection<? extends S> subIds) {
        return subIds.stream().map(subId -> create(mainId, subId)).collect(Collectors.toList());
    }

}
